﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using DataEntities;
using DataProvider;

namespace SQLDataProvider
{
    public class BaseRepository<T> : IBaseRepository<T> where T : Entity
    {
        #region Get method

        public virtual T Get(Func<T, bool> condition)
        {
            using (var db = new RepositoryContext())
            {
                return db.GetTable<T>().FirstOrDefault(condition);
            }
        }

        public virtual T Get(Func<T, bool> condition, Func<DataLoadOptions> howToGetLoadOptions)
        {
            using (var db = new RepositoryContext())
            {
                db.LoadOptions = howToGetLoadOptions();
                return db.GetTable<T>().FirstOrDefault(condition);
            }
        }

        public virtual List<TResult> Get<TResult>(Func<T, TResult> selector)
        {
            using (var db = new RepositoryContext())
            {
                return db.GetTable<T>().Select(selector).ToList();
            }
        }

        public virtual List<TResult> Get<TResult>(Func<T, TResult> selector, Func<T, bool> condition)
        {
            using (var db = new RepositoryContext())
            {
                return db.GetTable<T>().Where(condition).Select(selector).ToList();
            }
        }

        public virtual List<T> GetAll(Func<T, bool> condition)
        {
            using (var db = new RepositoryContext())
            {
                return db.GetTable<T>().Where(condition).ToList();
            }
        }

        public virtual List<T> GetAll(Func<T, bool> condition, int start, int take)
        {
            using (var db = new RepositoryContext())
            {
                return db.GetTable<T>().Where(condition).Skip(start).Take(take).ToList();
            }
        }

        public virtual List<T> GetAll(Func<T, bool> condition, Func<DataLoadOptions> howToGetLoadOptions)
        {
            using (var db = new RepositoryContext())
            {
                db.LoadOptions = howToGetLoadOptions();
                return db.GetTable<T>().Where(condition).ToList();
            }
        }

        public virtual List<T> GetAll(Func<T, bool> condition, Func<DataLoadOptions> howToGetLoadOptions, int start, int take)
        {
            using (var db = new RepositoryContext())
            {
                db.LoadOptions = howToGetLoadOptions();
                return db.GetTable<T>().Where(condition).Skip(start).Take(take).ToList();
            }
        }

        #endregion

        #region Add method

        public virtual T Add(T entity)
        {
            using (var db = new RepositoryContext())
            {
                db.GetTable<T>().InsertOnSubmit(entity);
                db.SubmitChanges();
                return entity;
            }
        }

        public virtual List<T> Add(List<T> entities)
        {
            using (var db = new RepositoryContext())
            {
                db.GetTable<T>().InsertAllOnSubmit(entities.AsEnumerable());
                db.SubmitChanges();
                return entities;
            }
        }

        public virtual T Add(T entity, Func<DataLoadOptions> howToGetLoadOptions)
        {
            using (var db = new RepositoryContext())
            {
                db.LoadOptions = howToGetLoadOptions();
                db.GetTable<T>().InsertOnSubmit(entity);
                db.SubmitChanges();
                return entity;
            }
        }

        #endregion

        #region Update method

        public virtual T Update(Func<T, bool> condition, T newEntity)
        {
            using (var db = new RepositoryContext())
            {
                var tEntity = db.GetTable<T>().First(condition);
                db.UpdateEntity(tEntity, newEntity);
                db.SubmitChanges();
                return tEntity;
            }
        }

        public virtual T Update(Func<T, bool> condition, T newEntity, string[] includeProperties)
        {
            using (var db = new RepositoryContext())
            {
                var tEntity = db.GetTable<T>().First(condition);
                db.UpdateEntity(tEntity, newEntity, includeProperties);
                db.SubmitChanges();
                return tEntity;
            }
        }

        public virtual T Update(Func<T, bool> condition, Dictionary<string, object> updateField)
        {
            using (var db = new RepositoryContext())
            {
                var tEntity = db.GetTable<T>().First(condition);
                db.UpdateEntity(tEntity, updateField);
                db.SubmitChanges();
                return tEntity;
            }
        }

        public virtual List<T> Update(Func<T, bool> condition, Action<IEnumerable<T>> howToUpdateEntity)
        {
            using (var db = new RepositoryContext())
            {
                var tEntity = db.GetTable<T>().Where(condition);
                howToUpdateEntity(tEntity);
                db.SubmitChanges();
                return tEntity.ToList();
            }
        }

        public virtual T Update(Func<T, bool> condition, Action<T> howToUpdateEntity)
        {
            using (var db = new RepositoryContext())
            {
                var tEntity = db.GetTable<T>().FirstOrDefault(condition);
                if (tEntity == null)
                {
                    return default(T);
                }
                howToUpdateEntity(tEntity);
                db.SubmitChanges();
                return tEntity;
            }
        }


        public virtual List<T> UpdateAll(Func<T, bool> condition, Action<T> howToUpdateEntity)
        {
            using (var db = new RepositoryContext())
            {
                var tEntity = db.GetTable<T>().Where(condition);
                foreach (var entity in tEntity)
                {
                    howToUpdateEntity(entity);
                }
                db.SubmitChanges();
                return tEntity.ToList();
            }
        }

        #endregion

        #region Delete method

        public virtual bool Delete(Func<T, bool> condition)
        {
            using (var db = new RepositoryContext())
            {
                try
                {
                    var entity = db.GetTable<T>().Where(condition);
                    if (entity != null)
                    {
                        db.GetTable<T>().DeleteAllOnSubmit(entity);
                        db.SubmitChanges();
                        return true;
                    }
                    return false;
                }
                catch (Exception error)
                {
                    throw new Exception(error.Message);
                }
            }
        }

        public virtual bool Delete(Func<T, bool> condition, Func<T, int> keys, string primaryKey)
        {
            if (String.IsNullOrEmpty(primaryKey))
                return false;
            using (var db = new RepositoryContext())
            {
                var ids = db.GetTable<T>().Where(condition).Select(keys).ToList();
                if (ids.Count != 0)
                {
                    var tbName = typeof(T).Name;
                    var keysStr = string.Empty;
                    ids.ForEach(p => keysStr += p + ",");
                    var commend = "delete from " + tbName + " where " + primaryKey + " in(" + keysStr.TrimEnd(',') + ")";
                    db.ExecuteCommand(commend);
                    return true;
                }
                return false;
            }
        }

        #endregion

        public virtual int Count(Func<T, bool> condition)
        {
            using (var db = new RepositoryContext())
            {
                return db.GetTable<T>().Count(condition);
            }
        }
    }
}
